package com.unitd.frame.comm.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.thoughtworks.xstream.XStream;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @desc 对象工具类
 * @filename ObjectUtils.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2016/11/13
 */
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {

	private final static Logger logger = LoggerFactory.getLogger(BeanUtils.class);

	/**
	 * @desc 判断对象是否为Null
	 * @param obj 需要进行判断的对象
	 * @return boolean
	 */
	public static boolean isNull(Object obj) {
		return obj == null;
	}

	/**
	 * @desc 将字符串类型的Object对象转换为字符串
	 * @param obj 需要转换的Object对象
	 * @return String
	 */
	public static String toStr(Object obj) {
		if(BeanUtils.isNull(obj)) return null;

		String val =  String.valueOf(obj);
		if(StringUtils.isNull(val)) return null;
		return val;
	}

	/**
	 * @desc 将数字转换成字符串
	 * @param obj 需要转换成字符串的数字
	 * @param defVal 默认值
	 * @return String
	 */
	public static String toStr(Object obj, String defVal) {
		if(BeanUtils.isNull(obj)) return defVal;

		String val =  String.valueOf(obj);
		if(StringUtils.isNull(val)) return defVal;
		return val;
	}

	/**
	 * @desc 将Object转换为int类型
	 * @param obj 字符串数据
	 */
	public static int toInt(Object obj) {
		if (isNull(obj)) {
			return 0;
		} else {
			if(obj instanceof Integer){
				return ((Integer) obj).intValue();
			} else if(obj instanceof Long) {
				return ((Long) obj).intValue();
			} else {
				return Integer.parseInt((String) obj);
			}
		}
	}

	/**
	 * @desc 将Object转换为long类型
	 * @param obj 字符串数据
	 */
	public static long toLong(Object obj) {
		if (isNull(obj)) {
			return 0L;
		} else {
			return (long)obj;
		}
	}

	/**
	 * @desc 将Object转换为float类型
	 * @param obj 字符串数据
	 */
	public static float toFloat(Object obj) {
		if (isNull(obj)) {
			return 0f;
		} else {
			return (float)obj;
		}
	}

	/**
	 * @desc 将Object转换为double类型
	 * @param obj 字符串数据
	 */
	public static double toDouble(Object obj) {
		if (isNull(obj)) {
			return 0D;
		} else {
			if(obj instanceof BigDecimal) {
				return ((BigDecimal) obj).doubleValue();
			} else {
				return (Double) obj;
			}
		}
	}

	/**
	 * @desc 将Object转换为date类型
	 * @param obj 字符串数据
	 */
	public static Date toDate(Object obj) {
		if (isNull(obj)) {
			return null;
		} else {
			return (Date) obj;
		}
	}

	/**
	 * @desc 对象转JSON
	 * @param obj 需要进行转换的对象
	 * @return json串
	 */
	public static String toJson(Object obj) {
		if (obj == null) {
			return null;
		}
		return JSON.toJSONString(obj);
	}

	/**
	 * @desc 按照指定的格式化类型,将对象格式化为JSON格式
	 * @param obj 需要进行格式化的对象
	 * @param type  格式化类型
	 * @return json串
	 */
	public static String toJsonOfType(Object obj, SerializerFeature type) {
		if (obj == null) {
			return null;
		}
		return JSON.toJSONString(obj, type);
	}

	/**
	 * @desc 对象转XML
	 * @param obj 需要进行转换的对象
	 * @return xml串
	 */
	public static String toXml(Object obj) {
		if (obj == null) {
			return null;
		}
		XStream stream = new XStream();
		stream.autodetectAnnotations(true);
		return stream.toXML(obj);
	}

	/**
	 * @desc 将JavaBan实体类转为Map类型,然后返回一个map类型的值(利用Introspector和PropertyDescriptor将Bean --> Map)
	 * @param obj 需要进行转换的对象
	 * @return Map对象
	 */
	public static Map<String, Object> toMap(Object obj) {
		Map<String, Object> params = new HashMap<>(0);
		String name = null;
		try {
			PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
			PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(obj);

			for (PropertyDescriptor descriptor : descriptors) {
				name = descriptor.getName();
				if (!"class".equals(name)) {
					params.put(name, propertyUtilsBean.getNestedProperty(obj, name));
				}
			}
		} catch (Exception e) {
			logger.warn("Java对象转换成Map对象失败" + e + ",KEY:" + name);
			e.printStackTrace();
		}
		return params;
	}

	/**
	 * @desc 复制对象
	 * @param obj 需要复制的对象
	 * @return 泛型对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T cpoy(Object obj) {
		try {
			ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(byteOut);
			out.writeObject(obj);

			ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
			ObjectInputStream in = new ObjectInputStream(byteIn);
			return (T) in.readObject();
		} catch (Exception e) {
			logger.warn("对象拷贝失败", e.getMessage());
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * @desc 通过反射给指定对象为空的属性设置值为null
	 * @param bean 需要设置值的对象
	 */
	public static void setClassPropDefValue(Object bean) {

		// 获取类的字节文件对象
		Class cls = bean.getClass();
		// 获取该类的所有成员变量
		Field[] fields = cls.getDeclaredFields();
		Method[] methods = cls.getDeclaredMethods();

		for (Field field : fields) {
			try {
				String fieldGetName = parGetName(field.getName());
				if (!checkGetMet(methods, fieldGetName)) {
					continue;
				}
				Method fieldGetMet = cls.getMethod(fieldGetName, new Class[]{});
				Object fieldVal = fieldGetMet.invoke(bean, new Object[]{});
				if (fieldVal != null) {
					if ("".equals(fieldVal)) {
						// 设置些属性是可以访问的
						// 给变量赋值
						field.setAccessible(true);
						field.set(bean, null);
					}
				}
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
	}

	/**
	 * @desc 拼接某属性的 get方法
	 * @param fieldName 属性名称
	 * @return String
	 */
	public static String parGetName(String fieldName) {
		if (null == fieldName || "".equals(fieldName)) {
			return null;
		}
		int startIndex = 0;
		if (fieldName.charAt(0) == '_')
			startIndex = 1;
		return "get"
				+ fieldName.substring(startIndex, startIndex + 1).toUpperCase()
				+ fieldName.substring(startIndex + 1);
	}

	/**
	 * @desc 判断是否存在某属性的 get方法
	 * @param methods 类的方法
	 * @param fieldGetMet get方法名
	 * @return boolean
	 */
	public static boolean checkGetMet(Method[] methods, String fieldGetMet) {
		for (Method met : methods) {
			if (fieldGetMet.equals(met.getName())) {
				return true;
			}
		}
		return false;
	}
}